
package io.renren.utils.util.db.transaction;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import io.renren.utils.util.db.connection.DBConnectionManager;


public class DBTransaction extends BaseTransaction {

    // ----------------------------------------------------- Static Variables
    final public static String PREFIX = "DBTransaction";
    static Logger logger = Logger.getLogger(DBTransaction.class.getName());

    // ----------------------------------------------------- Instance Variables
    private Connection conn;
    private Map preparedStatementMap;

    // ----------------------------------------------------------- Constructors


    /**
     * Constructs a new DBTransaction.
     *
     * @param name Name of the DBTransaction.
     */
    public DBTransaction(Connection conn) throws TransactionException {
        super();
        this.conn = conn;
        if (logger.isDebugEnabled())
            logger.debug("New DBTransaction ");
        reset();
    }


//    /**
//     * Constructs a new DBTransaction.
//     *
//     * @param name Name of the DBTransaction.
//     * @throws SQLException 
//     */
//    public DBTransaction(DataSource ds) throws TransactionException, SQLException {
//    	this(ds.getConnection());
//    }
//    
    private void reset() throws TransactionException {
        if (conn == null)
            throw new TransactionException("Failed to get a connection from the pool. ");
        preparedStatementMap = new TreeMap();
    }


    public Connection getConnection() {
        return conn;
    }

    public PreparedStatement prepareStatement(String name, String sql) throws SQLException {
        PreparedStatement retVal = (PreparedStatement) preparedStatementMap.get(name);
        if (retVal == null) {
            if (logger.isDebugEnabled())
                logger.debug("Creating ps - name " + name + " value " + sql);

            if (conn == null)
                throw new SQLException("Cannot prepare statement for null connection");

            retVal = getConnection().prepareStatement(sql);
            preparedStatementMap.put(name, retVal);
        }
        return retVal;
    }

    public PreparedStatement prepareStatement(String name) {
        PreparedStatement retVal = (PreparedStatement) preparedStatementMap.get(name);
        return retVal;
    }

    private void executeBatch() throws SQLException {
        Iterator it = preparedStatementMap.keySet().iterator();
        while (it.hasNext()) {
            String name = (String) it.next();
            logger.warn("Running " + name);
            PreparedStatement ps = (PreparedStatement) preparedStatementMap.get(name);
            int[] retVals = ps.executeBatch();
            String logStr = "Result = [";
            for (int i = 0; i < retVals.length; i++)
                logStr = logStr + retVals[i];
            logger.warn(logStr + "]");
        }
    }

    private void closeBatch() {
        Iterator it = preparedStatementMap.values().iterator();
        while (it.hasNext()) {
            PreparedStatement ps = (PreparedStatement) it.next();
            try {
                ps.clearBatch();
                ps.close();
            } catch (Exception e) {
                logger.warn("Unable to close the Prepared Statement", e);
            }
        }
        preparedStatementMap.clear();

    }

    // --------------------------------------------------------- Public Methods


    /**
     * 
     */
    public void commit() throws TransactionException {
    	/// only for debug
//        if (logger.isDebugEnabled()) {
//            try {
//                throw new Exception("Commiting DBTransaction Name : " + getName() + " at " + hashCode());
//            } catch (Exception ee) {
//                logger.debug("", ee);
//            }
//        }
        //////

        try {
            super.commit();

            if (conn == null)
                throw new TransactionException("Cannot commit with a null connection " + getName());
            io.renren.utils.util.db.connection.DBConnection c = (io.renren.utils.util.db.connection.DBConnection)getConnection();
        	logger.debug("Calling commit for TX 44 - " + c.getPoolName()  + " at " + c);
        	
            getConnection().commit();
        } catch (SQLException sqle) {
            logger.error("Unable to commit connection  " + getName(), sqle);
            throw new TransactionException("Cannot commit DB Transaction " + sqle);
        } catch (Exception e) {
            logger.error("Unable to commit connection  " + getName(), e);
            throw new TransactionException("Cannot commit DB Transaction " + e);
        } finally {
            // In case of Exception or after successful completion of the transaction
            // the Map needs to be cleared of its contents.
            closeBatch();
            freeConnection();
        }
    }

    /**
     * 
     */
    public void rollback() throws TransactionException {
    	//// only for debug
//        if (logger.isDebugEnabled()) {
//            try {
//                throw new Exception("Rolling back DBTransaction Name : " + getName() + " at " + hashCode());
//            } catch (Exception ee) {
//                logger.debug("", ee);
//            }
//        }
        /////////

        super.rollback();
        if (conn != null) {
            try {
                getConnection().rollback();
            } catch (SQLException sqle) {
                logger.error("Unable to rollback connection  " + getName(), sqle);
                throw new TransactionException("Cannot rollback DB Transaction " + sqle);
            } catch (Exception e) {
                logger.error("Unable to commit connection  " + getName(), e);
                throw new TransactionException("Cannot commit DB Transaction " + e);
            } finally {
                // Clear the Prepared Statement Map
                closeBatch();
                freeConnection();
            }
        }
    }

    public void freeConnection() {
    	if (logger.isDebugEnabled()) {
    		logger.debug("Transaction freeConnection ! "+ conn.hashCode());
    	}
    	try {
    		if (conn!=null) {
	    		conn.setAutoCommit(true);
				conn.close();
    		}
		} catch (SQLException e) {
			logger.error("Unable to free connection!", e);
		}
        conn = null;
    }


	public void begin() throws TransactionException {
		try {
			getConnection().setAutoCommit(false);
		} catch (SQLException e) {
			throw new TransactionException("Transaction begin error !", e);
		}
	}

	public void setRollbackOnly() {
		//TODO: FIX ME
	}

	public void setTransactionTimeout(int arg0) {
		//TODO: FIX ME
	}

	public int getStatus() {
		//TODO: FIX ME
		return 0;
	}

}// DBTransaction









